//
//  KQChatViewController.swift
//  JChat
//
//  Created by JIGUANG on 2017/2/28.
//  Copyright © 2017年 HXHG. All rights reserved.
//

import UIKit
import MobileCoreServices
import CoreMedia
import AVFoundation
import IQKeyboardManager
import KQPhotoPicker
import KQToast
import KQChatSDK
import KQKSPhotoBrowser
import KQProgressHUD
open class KQChatViewController: KQChatBaseViewController, KQEmoticonInputViewDataSource, KQEmoticonInputViewDelegate, KQRecordVoiceHelperDelegate, KQChatViewDelegate, KQMessageDelegate, SAIToolboxInputViewDataSource, SAIToolboxInputViewDelegate, UIDocumentInteractionControllerDelegate, SAIInputBarDelegate, SAIInputBarDisplayable, AVAudioPlayerDelegate {
    
    /// 会话
    open var conversation: WFCCConversationInfo?
    /// 是否为群聊
    fileprivate var isGroup = false
    /// 工具条的类型，根据不同会话类型决定
    public var barType : SAIInputBarType = .default
    /// 底部聊天工具栏
    fileprivate lazy var toolbar: SAIInputBar = SAIInputBar(type: self.barType)
    /// 表情view和“+”view
    fileprivate lazy var inputViews: [String: UIView] = [:]
    fileprivate weak var inputItem: SAIInputItem?
    /// 聊天view
    var chatView: KQChatView!
    /// 聊天内容布局
    var chatViewLayout: KQChatViewLayout = .init()
    /// 提醒
    fileprivate lazy var reminds: [KQRemind] = []
    fileprivate lazy var documentInteractionController = UIDocumentInteractionController()
    
    /// 我的头像
    fileprivate var myAvator: UIImage?
    /// 消息列表
    lazy var messages: [KQMessage] = []
    /// 当前用户
    fileprivate let currentUser: WFCCUserInfo? = nil
    /// 消息当前页
    fileprivate var messagePage = 0
    fileprivate var currentMessage: KQMessageType!
    fileprivate var maxTime = 0
    fileprivate var minTime = 0
    fileprivate var minIndex = 0
    fileprivate var jMessageCount = 0
    fileprivate var isFristLaunch = true
    fileprivate var recordingHub: KQRecordingView!
    
    var player: AVAudioPlayer?
    
    
    //仅限于在Channel内使用。Channel的owner对订阅Channel单个用户发起一对一私聊
    var privateChatUser: String?
    open override func viewDidLoad() {
        super.viewDidLoad()
        _init()
    }
    
    open override func loadView() {
        super.loadView()
        let frame = CGRect(x: 0, y: 0, width: KQCHAT_SCREEN_WIDTH, height: 100)
        chatView = KQChatView(frame: frame, chatViewLayout: chatViewLayout)
        chatView.delegate = self
        chatView.messageDelegate = self
        toolbar.translatesAutoresizingMaskIntoConstraints = false
        if let chatUser = WFCCIMService.sharedWFCIM().getUserInfo(self.conversation?.conversation.target, refresh: false) {
            if chatUser.userId != "0000" {
                toolbar.delegate = self
                toolbar.text = self.conversation?.draft
            }
        }
    }
    
    open override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        toolbar.isHidden = false
        IQKeyboardManager.shared().isEnableAutoToolbar = false
        IQKeyboardManager.shared().isEnabled = false
    }
    
    open override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
        IQKeyboardManager.shared().isEnableAutoToolbar = true
        IQKeyboardManager.shared().isEnabled = true
    }
    
    open override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        guard self.conversation != nil else {
            return
        }
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardFrameChanged(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
        if self.conversation?.conversation.type == .Group_Type {
            let userInfo = WFCCIMService.sharedWFCIM().getUserInfo(self.conversation?.conversation.target, refresh: false)
            self.title = userInfo?.groupAlias
        } else if let single = WFCCIMService.sharedWFCIM().getUserInfo(self.conversation?.conversation.target, refresh: false) {
            self.title = single.displayName
        }
    }
    
    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        guard self.conversation != nil else {
            return
        }
        WFCCIMService.sharedWFCIM().setConversation(self.conversation?.conversation, draft: toolbar.text)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: - Notification
    
    func addNotification() {
        
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: kUpdateConversation), object: nil)
                
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardFrameChanged(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(onMessageListChanged(_ :)), name: Notification.Name.init(rawValue: "NSNotification"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessages(_ :)), name: Notification.Name.init(rawValue: kReceiveMessages), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onMessageDelivered(_ :)), name: Notification.Name.init(rawValue: kMessageDelivered), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onMessageReaded(_ :)), name: Notification.Name.init(rawValue: kMessageReaded), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onMessageUpdated(_ :)), name: Notification.Name.init(rawValue: kMessageUpdated), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(onSettingUpdated(_ :)), name: Notification.Name.init(rawValue: kSettingUpdated), object: nil)
    }
    
    
    
    // MARK: - UI
    private func _init() {
        
        self.navigationController?.isNavigationBarHidden = false
        
        player = KQAudioPlayerHelper.sharedInstance.player
        
        guard let conversation = self.conversation else {
            return
        }
            
        myAvator = UIImage.getMyAvator()
        isGroup = self.conversation?.conversation.type == .Group_Type ? true:false
        _updateTitle()
        view.backgroundColor = KQCHAT_COLOR(242, 242, 242)
        view.addSubview(chatView)
        
        chatView.snp.makeConstraints { make in
            if #available(iOS 11.0, *) {
                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
            } else {
                make.top.equalTo(self.topLayoutGuide.snp.top).offset(20)
            }
            make.left.equalToSuperview()
            make.right.equalToSuperview()
            make.bottom.equalToSuperview().offset(-iphoneXOffsetY)
        }
        
        WFCCIMService.sharedWFCIM().clearUnreadStatus(self.conversation?.conversation)
    
        self.addNotification()
        self.reloadMessageList(0)
    }
    @objc func onMessageListChanged(_ notification: Notification) {
//        self.reloadMessageList()
    }
    /// 接收消息
    @objc func onReceiveMessages(_ notification: Notification) {
        let messageArr: [WFCCMessage] = notification.object as! [WFCCMessage]
//        self.appendMessages(messages: messages, newMessage: true, highlightId: 0, forceButtom: false)
//        WFCCIMService.sharedWFCIM().clearMessages(conversation?.conversation)
        
        guard let conversation = self.conversation else {
            return
        }
        
        for i in 0..<messageArr.count {
            let message = messageArr[i]
            if isNeedInsertTimeLine(Int(message.serverTime)) {
                let timeContent = KQMessageTimeLineContent(date: Date(timeIntervalSince1970: TimeInterval(message.serverTime / 1000)))
                let m = KQMessage(content: timeContent)
                m.options.showsTips = false
                messages.append(m)
                chatView.append(m)
            }
            
            let mess = _parseMessage(message)
            
            if messages.contains(where: { (m) -> Bool in
                return m.msgId == mess.msgId
            }) {
                let indexs = chatView.indexPathsForVisibleItems
                for index in indexs {
                    var m = messages[index.row]
                    if m.msgId.count > 0 {
                        m = _parseMessage(WFCCIMService.sharedWFCIM().getMessage(Int(m.msgId)!), false)
                        chatView.update(m, at: index.row)
                    }
                }
                return
            }
            
            messages.append(mess)
            chatView.append(mess)
            updateUnread([mess])
        }
        
        
        WFCCIMService.sharedWFCIM().clearUnreadStatus(conversation.conversation)
        if !chatView.isRoll {
            chatView.scrollToLast(animated: false)
        }
    }
   
    @objc func onMessageDelivered(_ notification: Notification) {
        
    }
    @objc func onMessageReaded(_ notification: Notification) {
        
    }

    @objc func onMessageUpdated(_ notification: Notification) {
        
    }
    @objc func onSettingUpdated(_ notification: Notification) {
        
    }
    
    func reloadMessageList(_ page: Int) {

        guard let messageList = WFCCIMService.sharedWFCIM().getMessages(conversation?.conversation, contentTypes: nil, from: 0, count: 15, withUser: "") else { return }
            
        var msgs: [KQMessage] = []

        for index in 0..<messageList.count {
            let message = messageList[index]
            let msg = _parseMessage(message)
            msgs.insert(msg, at: 0)
            if isNeedInsertTimeLine(Int(message.serverTime)) || index == messages.count - 1 {
                let timeContent = KQMessageTimeLineContent(date: Date(timeIntervalSince1970: TimeInterval(message.serverTime / 1000)))
                let m = KQMessage(content: timeContent)
                m.options.showsTips = false
                msgs.insert(m, at: 0)
            }
            
        }
        if page != 0 {
            minIndex = minIndex + msgs.count
            chatView.insert(contentsOf: msgs, at: 0)
        } else {
            minIndex = msgs.count - 1
            chatView.append(contentsOf: msgs)
        }
        self.messages.insert(contentsOf: msgs, at: 0)
        
        if messages.count < 17 {
            chatView.stopRefreshWithNoMoreData()
        } else {
            chatView.stopRefresh()
        }
            

    }
    
    func loadMoreMessage(isHistory: Bool = false, completion: ((_ more: Bool)->Void)) {
        if isHistory {
            
        }
    }
    
    

    // MARK: - 通知
    /// 更新附件通知
//    @objc func _updateFileMessage(_ notification: Notification) {
//        guard let conversation = self.conversation else {
//            return
//        }
//        let userInfo = notification.userInfo
//        let msg = userInfo?[kUpdateFileMessage] as! JMSGMessage
//        let msgId = msg.msgId
//        let message = conversation.message(withMessageId: msgId)!
//        let content = message.content as! JMSGFileContent
//        let url = URL(fileURLWithPath: content.originMediaLocalPath ?? "")
//        var data: Data?
//        do {
//            data = try Data(contentsOf: url)
//        } catch let error as NSError {
//            SVProgressHUD.showToast(error.localizedDescription)
//            return
//        }
//        updateMediaMessage(message, data: data!)
//    }

    /// 更新消息通知
    @objc func _reloadMessage() {
        _removeAllMessage()
        messagePage = 0
        _loadMessage(messagePage)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
            self.chatView.scrollToLast(animated: false)
        }
    }
    
    /// 移除消息通知
    @objc func _removeAllMessage() {
        jMessageCount = 0
        messages.removeAll()
        chatView.removeAll()
    }
    
    /// 点击页面通知
    @objc func _tapView() {
        view.endEditing(true)
        toolbar.resignFirstResponder()
    }
    
    /// 键盘变化通知
    @objc func keyboardFrameChanged(_ notification: Notification) {
        let dic = NSDictionary(dictionary: (notification as NSNotification).userInfo!)
        let keyboardValue = dic.object(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue
        let bottomDistance = UIScreen.main.bounds.size.height - keyboardValue.cgRectValue.origin.y
        let duration = Double(truncating: dic.object(forKey: UIResponder.keyboardAnimationDurationUserInfoKey) as! NSNumber)
        
        UIView.animate(withDuration: duration, animations: {
        }) { (finish) in
            if (bottomDistance == 0 || bottomDistance == self.toolbar.height) && !self.isFristLaunch {
                return
            }
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
                self.chatView.scrollToLast(animated: false)
            }
            self.isFristLaunch = false
        }
    }
    
    // MARK: - methods
    /// 返回
    @objc func _back() {
        navigationController?.popViewController(animated: true)
    }
    
    /// 更新标题
    private func _updateTitle() {
        guard let conversation = self.conversation else {
            return
        }
        if self.conversation?.conversation.type == .Group_Type {
            if let group = WFCCIMService.sharedWFCIM().getGroupInfo(conversation.conversation.target, refresh: false) {
                title = group.name
            }
        } else if self.conversation?.conversation.type == .Single_Type {
            
            if let user = WFCCIMService.sharedWFCIM().getUserInfo(conversation.conversation.target, refresh: false) {
                if user.friendAlias.count > 0 {
                    title = user.friendAlias
                } else if user.displayName.count > 0 {
                    title = user.displayName
                } else {
                    title = conversation.conversation.target
                }
            }
        }
    
    }
    
    /// 分页加载消息
    fileprivate func _loadMessage(_ page: Int) {
        printLog("\(page)")
        guard let conversation = self.conversation else {
            return
        }
        
        var lastIndex: Int = 0
        if self.messages.count > 0 {
            let mess = messages.first
            lastIndex = mess?.jmessage?.messageId ?? 0
        }
        
        let messagesList = WFCCIMService.sharedWFCIM().getMessages(conversation.conversation, contentTypes: nil, from: lastIndex, count: 17, withUser: "")
        
        if messagesList?.count == 0 {
            chatView.stopRefreshWithNoMoreData()
            return
        }
        var msgs: [KQMessage] = []
        for index in 0..<(messagesList?.count ?? 0) {
            guard let message = messagesList?[index] else{return}
            let msg = _parseMessage(message)
            msgs.insert(msg, at: 0)
            if isNeedInsertTimeLine(Int(message.serverTime)) || index == messages.count - 1 {
                let timeContent = KQMessageTimeLineContent(date: Date(timeIntervalSince1970: TimeInterval(message.serverTime / 1000)))
                let m = KQMessage(content: timeContent)
                m.options.showsTips = false
                msgs.insert(m, at: 0)
            }
        }
        if page != 0 {
            minIndex = minIndex + msgs.count
            chatView.insert(contentsOf: msgs, at: 0)
        } else {
            minIndex = msgs.count - 1
            chatView.append(contentsOf: msgs)
        }
        self.messages.insert(contentsOf: msgs, at: 0)

        if messages.count < 17 {
            chatView.stopRefreshWithNoMoreData()
        } else {
            chatView.stopRefresh()
        }
    }
    
    /// 判断是否需要插入时间
    private func isNeedInsertTimeLine(_ time: Int) -> Bool {
        if maxTime == 0 || minTime == 0 {
            maxTime = time
            minTime = time
            return true
        }
        if (time - maxTime) >= 5 * 60000 {
            maxTime = time
            return true
        }
        if (minTime - time) >= 5 * 60000 {
            minTime = time
            return true
        }
        return false
    }
    
    /// 更新媒体消息
    fileprivate func updateMediaMessage(_ message: WFCCMessage, data: Data?) {
        DispatchQueue.main.async {
            if let index = self.messages.index(message) {
                let msg = self.messages[index]
                if ((msg.jmessage?.content.isKind(of: WFCCImageMessageContent.self)) != nil) {
                    let imageContent = msg.content as! KQMessageImageContent
                    let image = UIImage(data: data!)
                    msg.content = imageContent
                }
                
                msg.updateSizeIfNeeded = true
                self.chatView.update(msg, at: index)
                msg.updateSizeIfNeeded = false
            }
        }
    }
    
    /// 更新会话未读消息数
    func _updateBadge() {
//        JMSGConversation.allConversations { (result, error) in
//            guard let conversations = result as? [JMSGConversation] else {
//                return
//            }
//            let count = conversations.unreadCount
//            if count == 0 {
//                self.leftButton.setTitle("会话", for: .normal)
//            } else {
//                self.leftButton.setTitle("会话(\(count))", for: .normal)
//            }
//        }
    }
    
    /// 解析消息
    fileprivate func _parseMessage(_ message: WFCCMessage, _ isNewMessage: Bool = true) -> KQMessage {
        if isNewMessage {
            jMessageCount += 1
        }
        return message.parseMessage(self, { [weak self] (message, data) in
            self?.updateMediaMessage(message, data: data)
        })
    }
    
    /// 跳转至相册
    private func _pushToSelectPhotos() {
        self.kq_presentAlbum(with: .photoAndVideo, editType: .none, maxCount: 9, openCamera: false, rememberSelect: false) { [weak self] (allList, imageList, videoList) in
            self?.sendImageOrVideoList(allList: allList, imageList: imageList, videoList: videoList)
        } cancel: {
            
        }
    }
    
    /// 跳转至相机
    private func _pushToCamera() {
        self.kq_presentCamera(with: .photoAndVideo, editType: .none, maxCount: 9, openCamera: false) { [weak self] (allList, imageList, videoList) in
            self?.sendImageOrVideoList(allList: allList, imageList: imageList, videoList: videoList)
        } cancel: {
            
        }
    }
    
    /// 发送多个图片or视频
    private func sendImageOrVideoList(allList: [Any], imageList: [UIImage], videoList: [URL]) {
        
        if allList.count == 0 {
            return
        }
        guard let conversation = self.conversation?.conversation else {
            return
        }
        for i in 0..<allList.count {
            let any = allList[i]
            if any is UIImage {
                let image = any as! UIImage
                /// 时间
                let now:Date = Date()
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "yyyyMMddhhmmss"
                let nowTime = dateFormatter.string(from: now)
                
                
                let cacheDir = WFCUConfigManager.global().cachePath(of: conversation, mediaType: .Media_Type_IMAGE)
                let path = cacheDir.appending("/\(nowTime)\(i).jpg")
                guard let imgContent = WFCCImageMessageContent.init(from: image, cachePath: path) else { return }

                self.sendMessageAction(content: imgContent)
            } else if any is URL {
                
                
                let videoURL = any as! URL
                /// 第一张图片
                let thumb = KQVideoManager.videoFirstFrame(videoURL, size: CGSize(width: JC_VIDEO_MSG_IMAGE_WIDTH, height: JC_VIDEO_MSG_IMAGE_HEIGHT))
                let avUrl = AVURLAsset(url: videoURL)
                /// 时长
                let time = avUrl.duration
                let seconds = ceil(Double(time.value)/Double(time.timescale))
            
                var url = videoURL.absoluteString
                url = url.replacingOccurrences(of: "file:///private", with: "")

                let avAsset = AVURLAsset.init(url: videoURL, options: nil)
                guard let compressionVideoPath = WFCCUtilities.getDocumentPath(withComponent: "/VIDEO") else { return }
                let exportSession = AVAssetExportSession.init(asset: avAsset, presetName: "AVAssetExportPresetMediumQuality")
                let formater = DateFormatter.init()
                formater.dateFormat = "yyyy-MM-dd_HH-mm-ss"
                
                let manager = FileManager.default
                
                let isExists = manager.fileExists(atPath: compressionVideoPath)
                if isExists {
                    try? manager.createDirectory(atPath: compressionVideoPath, withIntermediateDirectories: true, attributes: nil)
                }
                let resultPath = compressionVideoPath.appending("outputJFVideo-%\(formater.string(from: Date.init())).mov")
                
                exportSession?.outputURL = URL.init(fileURLWithPath: resultPath)
                
                exportSession?.outputFileType = .mp4
                
                exportSession?.shouldOptimizeForNetworkUse = true
                
                exportSession?.exportAsynchronously(completionHandler: {
                    if exportSession?.status == .completed {
                        guard let videoContent = WFCCVideoMessageContent.init(path: resultPath, thumbnail: thumb) else { return }
                        videoContent.duration = Int(seconds)
                        DispatchQueue.main.async {
                            self.sendMessageAction(content: videoContent)
                        }

                    } else {
                        // 压缩失败
                    }
                })

            }
            
        }
    }
    
    
    // MARK: - 发送消息
    /// 最终发送消息入口
    open func sendMessageAction(content: WFCCMessageContent) {
        guard let conversation = self.conversation else {
            return
        }
        
        let sendMessage = WFCCIMService.sharedWFCIM().send(conversation.conversation, content: content) { messageUid, timestamp, newMessage  in
            
            guard let index = self.messages.index(newMessage!) else {
                return
            }
            let message = self._parseMessage(newMessage!)
            message.options.state = .sendSucceed
            message.options.alignment = .right
            self.messages[index] = message
            self.chatView.update(message, at: index)
        } error: { error_code, newMessage in
            guard let index = self.messages.index(newMessage!) else {
                return
            }
            let message = self._parseMessage(newMessage!)
            message.options.state = .sendError
            self.messages[index] = message
            message.options.alignment = .right
            self.chatView.update(message, at: index)
        }
        
        
        guard let sendedMessage = sendMessage else {
            return
        }

        if isNeedInsertTimeLine(Int(sendedMessage.serverTime)) {
            let timeContent = KQMessageTimeLineContent(date: Date(timeIntervalSince1970: TimeInterval(sendedMessage.serverTime / 1000)))
            let m = KQMessage(content: timeContent)
            m.options.showsTips = false
            messages.append(m)
            chatView.append(m)

        }
        let message = _parseMessage(sendedMessage)
        message.options.alignment = .right
        if sendedMessage.status == .Message_Status_Sending {
            message.options.state = .sending
        } else if sendedMessage.status == .Message_Status_Sent {
            message.options.state = .sendSucceed
            
        } else if sendedMessage.status == .Message_Status_Send_Failure {
            message.options.state = .sendError
        }
        
        
        message.targetType = .single
        message.unreadCount = 1
        
        messages.append(message)
        chatView.append(message)
        chatView.scrollToLast(animated: false)
        
        
    }
    
  
    /// 发送文本
    func send(forText attributedText: NSAttributedString, text: String) {
        guard let _ = self.conversation else {
            return
        }

        if text.length == 0 {
            return;
        }
        
        let content = WFCCTextMessageContent.init()
        content.text = text
        self.sendMessageAction(content: content)
    }
    
    /// 发送大表情
    func send(forLargeEmoticon emoticon: KQCEmoticonLarge) {

    }
    
    /// 发送录音
    func send(voiceData: Data, voicePath: String, duration: Double) {
        guard let _ = self.conversation else {
            return
        }
        guard let _ = self.conversation?.conversation else {
            return
        }

        guard let content = WFCCSoundMessageContent.init(forWav: voicePath, destinationAmrPath: nil, duration: Int(duration)) else { return }
        content.localPath = voicePath
        self.sendMessageAction(content: content)
    }
    
    /// 发送表情
    @objc func _sendHandler() {
        guard let attributedText = toolbar.attributedText else { return}
        let text = toolbar.text
        if text != nil && (text?.length)! > 0 {
            send(forText: attributedText, text: text!)
            toolbar.attributedText = nil
            toolbar.text = nil
        }
    }

    
    
    // MARK: - KQChatViewDelegate（页面操作）
    /// 下拉加载更多
    public func refershChatView( chatView: KQChatView) {
        self.messagePage += 1
        self._loadMessage(self.messagePage)
    }
    
    /// 删除消息
    public func deleteMessage(message: KQMessageType) {
        
        guard let jmessage = message.jmessage else {
            return
        }
        WFCCIMService.sharedWFCIM().deleteMessage(jmessage.messageId)
        
        if let index = messages.index(message) {
            jMessageCount -= 1
            messages.remove(at: index)
            if let message = messages.last {
                if message.content is KQMessageTimeLineContent {
                    messages.removeLast()
                    chatView.remove(at: messages.count)
                }
            }
            
        }
        
    }
    
    /// 撤回消息
    public func withdrawMessage(message: KQMessageType) {
        guard let jmessage = message.jmessage else {
            return
        }
        WFCCIMService.sharedWFCIM().recall(jmessage) {
            
            if let index = self.messages.index(message) {
                let recallMessage = WFCCIMService.sharedWFCIM().getMessage(jmessage.messageId)
                let msg = self._parseMessage(recallMessage!, false)
                self.messages[index] = msg
                self.chatView.update(msg, at: index)
            }
            
        } error: { error in
            KQToast.show("发送时间过长，不能撤回", duration: 2)
        }
    }

    /// 可用的items
    public func indexPathsForVisibleItems(chatView: KQChatView, items: [IndexPath]) {
        for item in items {
            if item.row <= minIndex {
                var msgs: [KQMessage] = []
                for index in item.row...minIndex  {
                    msgs.append(messages[index])
                }
                updateUnread(msgs)
                minIndex = item.row
            }
        }
    }

    /// 更新未读
    fileprivate func updateUnread(_ messages: [KQMessage]) {
        guard let conversation = self.conversation else {
            return
        }
//        for message in messages {
//            if message.options.alignment != .left {
//                continue
//            }
//            if let msg = conversation.message(withMessageId: message.msgId) {
//                if msg.isHaveRead {
//                    continue
//                }
//                msg.setMessageHaveRead({ _,_  in
//                })
//            }
//        }
    }
    
    // MARK: - KQMessageDelegate（点击消息cell）
    /// 点击视频
    public func message(message: KQMessageType, videoData data: Data?) {
        if let data = data {
            KQVideoManager.playVideo(message: message, data: data, currentViewController: self)
        }
      }
   
    /// 点击图片
    public func message(message: KQMessageType, image: UIImage?) {
        guard let content = message.content as? KQMessageImageContent else {
            return
        }
        
        let url = URL(string: content.remoteUrl!)
        let itemModel = KSPhotoItem(sourceView: chatView, thumbImage: content.thumbnailImage, imageUrl: url)
        let browser = KSPhotoBrowser(photoItems: [itemModel] , selectedIndex: UInt(0))
        browser.pageindicatorStyle = KSPhotoBrowserPageIndicatorStyle.dot
        browser.backgroundStyle = .black
        browser.show(from: self)
        
    }
  
    /// 播放语音
    public func message(message: KQMessageType, voiceData data: Data?, duration: Double) {
        guard let data = data else {
            KQToast.show("播放失败",duration: 1)
            return
        }
        if let player = KQAudioPlayerHelper.sharedInstance.player {
            if player.isPlaying {
                KQAudioPlayerHelper.sharedInstance.stopAudio()
                return
            }
        }
        KQAudioPlayerHelper.sharedInstance.managerAudioWithData(data, toplay: true)
    }

    /// 点击重发按钮
    public func clickTips(message: KQMessageType) {
//        currentMessage = message
        hx_showAlert(self, "重新发送", "是否重新发送该消息？", "取消", "发送") {
            // 取消
        } _: { [weak self] in
            // 发送
            guard let self = self else {
                return
            }
            if let index = self.messages.index(message) {
                guard let _ = self.conversation else {
                    return
                }
                let timeContent = KQMessageTimeLineContent(date: Date(timeIntervalSince1970: TimeInterval(message.jmessage!.serverTime / 1000)))
                let m = KQMessage(content: timeContent)
                m.options.showsTips = false
                if self.messages.contains(m) {
                    print("")
                } else {
                    print("")
                }
                
                self.messages.remove(at: index)
                self.chatView.remove(at: index)
                if message.msgId.count == 0 {
                    return
                }
                guard let jmessage = message.jmessage else {
                    return
                }
                WFCCIMService.sharedWFCIM().deleteMessage(jmessage.messageId)
                self.sendMessageAction(content: jmessage.content)
                self.chatView.scrollToLast(animated: false)
            }
        }
    }

    /// 点击头像
    public func tapAvatarView(message: KQMessageType) {
        guard let conversation = self.conversation else {
            return
        }
        toolbar.resignFirstResponder()
        if message.options.alignment == .left {
//            let toUser = conversation.target as? JMSGUser
//            if (toUser?.username ?? "").count > 0 {
//
//            } else {
//                SVProgressHUD.showToast("未获取到用户ID")
//            }
        }
        return
    }

    /// 长按头像
    public func longTapAvatarView(message: KQMessageType) {
        if !isGroup || message.options.alignment == .right {
            return
        }
        toolbar.becomeFirstResponder()
        if let user = message.sender {
            toolbar.text.append("@")
        }
    }

    /// 点击自定义消息
    open func tapCustom(message: KQMessageType, nextType: CustomMessageNextType, param: Any?) {
        toolbar.resignFirstResponder()
        // TODO
            if nextType == .call {
                // 打电话
                let phone = param as? String
                guard let url = URL(string: "tel://\(phone ?? "")") else {
                    return
                }
                UIApplication.shared.openURL(url)
            } else if nextType == .jumpWebView {
                // 跳转到网页
                let url = param as? String
//                let webViewVC = KQBaseWebViewController.init(url: url ?? "")
//                self.navigationController?.pushViewController(webViewVC, animated: true)
            }
//        else if nextType == .jumpRepair {
//                // 跳转到工单详情
//                let repairId = param as? String
//                KQRouterManager.shared().getManager(protocolType: KQRepairKitManagerProtocol.self)?.openRepairDetailVC(from: self, repairId: repairId, animation: true)
//            } else if nextType == .pullAutoAnswer {
//                // 获取问题答案
//                if let param = param as? String {
//                    self.getIMFaq(questionID: Int64(param))
//                }
//            } else if nextType == .chatWithKeeper {
//                let model = param as? KQMessageHouseKeeperModel
//                self.jumpToNewSessionPage(userId: model?.userId)
//            }
        
    }
    
    open override var inputAccessoryView: UIView? {
        if let chatUser = WFCCIMService.sharedWFCIM().getUserInfo(conversation?.conversation.target, refresh: false) {
            if chatUser.displayName != "0000" {
                return toolbar
            }
        }
        return nil
    }
    open var scrollView: SAIInputBarScrollViewType {
        return chatView
    }
    open override var canBecomeFirstResponder: Bool {
        return true
    }
    
    // MARK: - 底部工具-输入框设置 SAIInputBarDelegate & SAIInputBarDisplayable
    open func inputView(with item: SAIInputItem) -> UIView? {
        if let view = inputViews[item.identifier] {
            return view
        }
        switch item.identifier {
        case "kb:emoticon":
            let view = KQEmoticonInputView()
            view.delegate = self
            view.dataSource = self
            inputViews[item.identifier] = view
            return view
        case "kb:toolbox":
            let view = SAIToolboxInputView()
            view.delegate = self
            view.dataSource = self
            inputViews[item.identifier] = view
            return view
        default:
            return nil
        }
    }
    
    open func inputViewContentSize(_ inputView: UIView) -> CGSize {
        return CGSize(width: view.frame.width, height: 216)
    }
    
    public func inputBar(_ inputBar: SAIInputBar, shouldDeselectFor item: SAIInputItem) -> Bool {
        return true
    }
    open func inputBar(_ inputBar: SAIInputBar, shouldSelectFor item: SAIInputItem) -> Bool {
        if item.identifier == "kb:audio" ||
            item.identifier == "kb:receiver:human" ||
            item.identifier == "kb:receiver:robot"{
            return true
        }
        guard let _ = inputView(with: item) else {
            return false
        }
        return true
    }
    open func inputBar(_ inputBar: SAIInputBar, didSelectFor item: SAIInputItem) {
        inputItem = item
        
        if item.identifier == "kb:audio" {
            inputBar.deselectBarAllItem()
            return
        }
        if let kb = inputView(with: item) {
            inputBar.setInputMode(.selecting(kb), animated: true)
        }
    }
    open func inputBar(didChangeMode inputBar: SAIInputBar) {
        if inputItem?.identifier == "kb:audio" ||
            inputItem?.identifier == "kb:receiver:human" ||
            inputItem?.identifier == "kb:receiver:robot"{
            return
        }
        if let item = inputItem, !inputBar.inputMode.isSelecting {
            inputBar.deselectBarItem(item, animated: true)
        }
    }
    
    open func inputBar(didChangeText inputBar: SAIInputBar) {
        _emoticonSendBtn.isEnabled = inputBar.attributedText.length != 0
    }
    
    public func inputBar(shouldReturn inputBar: SAIInputBar) -> Bool {
        if inputBar.attributedText.length == 0 {
            return false
        }
        if inputBar.text.length == 0 {
            return false
        }
        send(forText: inputBar.attributedText, text: inputBar.text)
        inputBar.attributedText = nil
        inputBar.text = nil
        return false
    }
    
    public func inputBar(_ inputBar: SAIInputBar, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        let currentIndex = range.location
        if !isGroup {
            return true
        }
        if string == "@" {

        } else {
//            return updateRemids(inputBar, string, range, currentIndex)
        }
        return true
    }

    
    
    public func inputBar(touchDown recordButton: UIButton, inputBar: SAIInputBar) {
        if recordingHub != nil {
            recordingHub.removeFromSuperview()
        }
        let permissionStatus = AVAudioSession.sharedInstance().recordPermission
        if permissionStatus == AVAudioSession.RecordPermission.undetermined {
            AVAudioSession.sharedInstance().requestRecordPermission { (granted) in

            }
        } else if permissionStatus == AVAudioSession.RecordPermission.denied {
            self.hw_OpenURL()
        } else {
            self.recordingHub = KQRecordingView(frame: CGRect.zero)
            self.recordHelper.updateMeterDelegate = self.recordingHub
            self.recordingHub.startRecordingHUDAtView(self.view)
            self.recordingHub.frame = CGRect(x: self.view.centerX - 70, y: self.view.centerY - 70, width: 136, height: 136)
            self.recordHelper.startRecordingWithPath(String.getRecorderPath()) {
            }
        }
    }
    
    public func inputBar(dragInside recordButton: UIButton, inputBar: SAIInputBar) {
        recordingHub.pauseRecord()
    }
    
    public func inputBar(dragOutside recordButton: UIButton, inputBar: SAIInputBar) {
        recordingHub.resaueRecord()
    }
    
    public func inputBar(touchUpInside recordButton: UIButton, inputBar: SAIInputBar) {
        if recordHelper.recorder ==  nil {
            return
        }
        recordHelper.finishRecordingCompletion()
        if (recordHelper.recordDuration! as NSString).floatValue < 1 {
            recordingHub.showErrorTips()
            let time: TimeInterval = 1.5
            let hub = recordingHub
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
                hub?.removeFromSuperview()
            }
            return
        } else {
            recordingHub.removeFromSuperview()
        }
        var data: Data?
        do {
            data = try Data(contentsOf: URL(fileURLWithPath: recordHelper.recordPath!))
        } catch let error as NSError {
            KQToast.show(error.localizedDescription, duration: 2)
            return
        }
        send(voiceData: data!, voicePath: recordHelper.recordPath!, duration: Double(recordHelper.recordDuration!)!)
    }
    
    public func inputBar(touchUpOutside recordButton: UIButton, inputBar: SAIInputBar) {
        recordHelper.cancelledDeleteWithCompletion()
        recordingHub.removeFromSuperview()
    }
    
    // MARK: - 底部工具-表情 KQEmoticonInputViewDataSource & KQEmoticonInputViewDelegate
    open func numberOfEmotionGroups(in emoticon: KQEmoticonInputView) -> Int {
        return _emoticonGroups.count
    }

    open func emoticon(_ emoticon: KQEmoticonInputView, emotionGroupForItemAt index: Int) -> KQEmoticonGroup {
        return _emoticonGroups[0]
    }

    open func emoticon(_ emoticon: KQEmoticonInputView, numberOfRowsForGroupAt index: Int) -> Int {
        return _emoticonGroups[0].rows
    }

    open func emoticon(_ emoticon: KQEmoticonInputView, numberOfColumnsForGroupAt index: Int) -> Int {
        return _emoticonGroups[0].columns
    }

    open func emoticon(_ emoticon: KQEmoticonInputView, moreViewForGroupAt index: Int) -> UIView? {
        if _emoticonGroups[index].type.isSmall {
            return _emoticonSendBtn
        } else {
            return nil
        }
    }
    
    open func emoticon(_ emoticon: KQEmoticonInputView, insetForGroupAt index: Int) -> UIEdgeInsets {
        return UIEdgeInsets.init(top: 12, left: 10, bottom: CGFloat(12 + 24), right: 10)
    }

    open func emoticon(_ emoticon: KQEmoticonInputView, didSelectFor item: KQEmoticon) {
        if item.isBackspace {
            toolbar.deleteBackward()
            return
        }
        if let emotitle = item as? KQCEmoticon {
            toolbar.insertText(emotitle.title)
            return
        }
        if let emoticon = item as? KQCEmoticonLarge {
            send(forLargeEmoticon: emoticon)
            return
        }
        if let code = item.contents as? String {
            return toolbar.insertText(code)
        }
        if let image = item.contents as? UIImage {
            let d = toolbar.font?.descender ?? 0
            let h = toolbar.font?.lineHeight ?? 0
            let attachment = NSTextAttachment()
            attachment.image = image
            attachment.bounds = CGRect(x: 0, y: d, width: h, height: h)
            toolbar.insertAttributedText(NSAttributedString(attachment: attachment))
            return
        }
    }
    
    // MARK: - 底部工具-多按钮页面 SAIToolboxInputViewDataSource & SAIToolboxInputViewDelegate
    open func numberOfToolboxItems(in toolbox: SAIToolboxInputView) -> Int {
        return _toolboxItems.count
    }
    open func toolbox(_ toolbox: SAIToolboxInputView, toolboxItemForItemAt index: Int) -> SAIToolboxItem {
        return _toolboxItems[index]
    }
    
    open func toolbox(_ toolbox: SAIToolboxInputView, numberOfRowsForSectionAt index: Int) -> Int {
        return 1
    }
    open func toolbox(_ toolbox: SAIToolboxInputView, numberOfColumnsForSectionAt index: Int) -> Int {
        return 4
    }
    
    open func toolbox(_ toolbox: SAIToolboxInputView, insetForSectionAt index: Int) -> UIEdgeInsets {
        return UIEdgeInsets.init(top: -50, left: 10, bottom: 12, right: 10)
    }
    open func toolbox(_ toolbox: SAIToolboxInputView, shouldSelectFor item: SAIToolboxItem) -> Bool {
        return true
    }
    
    open func toolbox(_ toolbox: SAIToolboxInputView, didSelectFor item: SAIToolboxItem) {
        self.toolbar.resignFirstResponder()
        switch item.identifier {
        case "page:pic":
            _pushToSelectPhotos()
        case "page:camera":
            _pushToCamera()
        default:
            break
        }
    }
    
    // MARK: - UIDocumentInteractionControllerDelegate
    public func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        return self
    }
    public func documentInteractionControllerViewForPreview(_ controller: UIDocumentInteractionController) -> UIView? {
        return view
    }
    public func documentInteractionControllerRectForPreview(_ controller: UIDocumentInteractionController) -> CGRect {
        return view.frame
    }
    
    
    // MARK: - KQRecordVoiceHelperDelegate（录音）
    /// 录音超出最大限制
    public func beyondLimit(_ time: TimeInterval) {
        recordHelper.finishRecordingCompletion()
        recordingHub.removeFromSuperview()
        var data: Data?
        do {
            data = try Data(contentsOf: URL(fileURLWithPath: recordHelper.recordPath!))
        } catch let error as NSError {
            KQToast.show(error.localizedDescription, duration: 2)
            return
        }
        send(voiceData: data!, voicePath: recordHelper.recordPath!, duration: Double(recordHelper.recordDuration!)!)
    }
    // MARK: - 跳转系统设置界面
    func hw_OpenURL() {
        let url = URL(string: UIApplication.openSettingsURLString)
        let alertController = UIAlertController(title: "麦克风权限未开启",
                                                message: "无法录制声音，前往“设置 > \(Bundle.main.infoDictionary!["CFBundleExecutable"]!)”中打开麦克风权限",
                                                preferredStyle: .alert)
        let cancelAction = UIAlertAction(title:"我知道了", style: .cancel, handler:nil)
        
        let settingsAction = UIAlertAction(title:"前往设置", style: .default, handler: {
            (action) -> Void in
            if  UIApplication.shared.canOpenURL(url!) {
                if #available(iOS 10, *) {
                    UIApplication.shared.open(url!, options: [:],completionHandler: {(success) in})
                } else {
                    UIApplication.shared.openURL(url!)
                }
            }
        })
        alertController.addAction(cancelAction)
        alertController.addAction(settingsAction)
        UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
    }
    
    
    
    // MARK: - lazy loading
    /// 表情组
    fileprivate lazy var _emoticonGroups: [KQCEmoticonGroup] = {
        var groups: [KQCEmoticonGroup] = []
        if let group = KQCEmoticonGroup(identifier: "qqEmoticons") {
            groups.append(group)
        }
        return groups
    }()
    
    /// 表情发送按钮
    fileprivate lazy var _emoticonSendBtn: UIButton = {
        var button = UIButton()
        button.titleLabel?.font = UIFont(name: KQCHAT_FONT_NAME, size: 15)
        button.contentEdgeInsets = UIEdgeInsets.init(top: 0, left: 18, bottom: 0, right: 18)
        button.setTitle("发送", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.setTitleColor(KQCHAT_COLOR(188, 188, 188), for: .disabled)
        button.setBackgroundImage(UIImage.loadImage("chat_emoticon_btn_send_blue"), for: .normal)
        button.setBackgroundImage(UIImage.loadImage("chat_emoticon_btn_send_gray"), for: .disabled)
        button.addTarget(self, action: #selector(_sendHandler), for: .touchUpInside)
        return button
    }()
    /// 表情包view
    fileprivate lazy var emoticonView: KQEmoticonInputView = {
        let emoticonView = KQEmoticonInputView(frame: CGRect(x: 0, y: 0, width: self.view.width, height: 275))
        emoticonView.delegate = self
        emoticonView.dataSource = self
        return emoticonView
    }()
    /// 照片、拍摄、报修
    fileprivate lazy var toolboxView: SAIToolboxInputView = {
        var toolboxView = SAIToolboxInputView(frame: CGRect(x: 0, y: 0, width: self.view.width, height: 197))
        toolboxView.delegate = self
        toolboxView.dataSource = self
        return toolboxView
    }()
    fileprivate lazy var _toolboxItems: [SAIToolboxItem] = {
        return [
            SAIToolboxItem("page:pic", "照片", UIImage.loadImage("chat_tool_pic")),
            SAIToolboxItem("page:camera", "拍摄", UIImage.loadImage("chat_tool_camera"))
            ]
    }()
    
    /// 录音
    fileprivate lazy var recordHelper: KQRecordVoiceHelper = {
        let recordHelper = KQRecordVoiceHelper()
        recordHelper.delegate = self
        return recordHelper
    }()
    
    fileprivate lazy var leftButton: UIButton = {
        let leftButton = UIButton(frame: CGRect(x: 0, y: 0, width: 90, height: 65 / 3))
        leftButton.setImage(UIImage.loadImage("com_icon_back"), for: .normal)
        leftButton.setImage(UIImage.loadImage("com_icon_back"), for: .highlighted)
        leftButton.addTarget(self, action: #selector(_back), for: .touchUpInside)
        leftButton.setTitle("会话", for: .normal)
        leftButton.titleLabel?.font = UIFont(name: KQCHAT_FONT_NAME, size: 16)
        leftButton.contentHorizontalAlignment = .left
        return leftButton
    }()

}

extension KQChatViewController: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        guard let view = touch.view else {
            return true
        }
        if view.isKind(of: KQMessageTextContentView.self) {
            return false
        }
        return true
    }
}
